#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <functional>
#include <unistd.h>
#include<pthread.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include"mythread.hpp"
#include"threadpool.hpp"

const int gsockfd = -1;
const u_int16_t gport = 8888;
const int gbacklog = 8;

using task_t = std::function<void(int,InetAddr)>;
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISEN_ERROR
};
class TcpServer
{
public:
    TcpServer(task_t Service,uint16_t port = gport)
        : _Service(Service),_port(port),_listensockfd(gsockfd), _isrunning(false)
    {
    }
    void Init()
    {
        // 创建tcp套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(FATAL, "sockfd create fall!\n");
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "create sockfd success,sockfd:%d\n", _listensockfd);
        // bind
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        int n = ::bind(_listensockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind false!\n");
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success!\n");
        // listen
        n = ::listen(_listensockfd, gbacklog);
        if (n < 0)
        {
            LOG(FATAL, "listen false!\n");
            exit(LISEN_ERROR);
        }
        LOG(INFO, "listen success!\n");
    }

    static void *Excute(void *args)
    {
        pthread_detach(pthread_self());
        ServerData *sd = static_cast<ServerData *>(args);
        sd->_td->_Service(sd->_sockfd,sd->_addr);
        delete sd;
        return nullptr;
    }

    class ServerData
    {
    public:
        ServerData(int sockfd, InetAddr& addr, TcpServer *td)
        :_sockfd(sockfd),_addr(addr),_td(td)
        {}

    public:
        int _sockfd;
        InetAddr _addr;
        TcpServer *_td;
    };
    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in client;
            memset(&client, 0, sizeof(client));

            socklen_t len = sizeof(client);
            int sockfd = accept(_listensockfd, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                LOG(ERROR, "accept false!\n");
                continue;
            }
            LOG(INFO, "connect a link! sockfd:%d\n", sockfd);
            InetAddr addr(client);

            pthread_t tid;
            ServerData* sd=new ServerData(sockfd,addr,this);
            pthread_create(&tid, nullptr, Excute, sd);
        }
    }

private:
    int _listensockfd;
    uint16_t _port;
    bool _isrunning;
    task_t _Service;
};